library(tidyverse)
library(car)
library(performance)
library(patchwork)

Research questions

Data Management

# load libraries
library(tidyverse) # for all things!
library(psych) # good for descriptive stats
library(patchwork) # grouping plots together
library(kableExtra) # useful for creating nice tables
library(sjPlot) #regression tables & plots
library(emmeans) #for contrasts
library(car) #for assumptions (crPlots, residualPlots, VIF) and bootstrapping

# read in datasets
data1 <- read_csv("https://uoepsy.github.io/data/DapR2_S1B2_PracticalPart1.csv")
data2 <- read_csv("https://uoepsy.github.io/data/DapR2_S1B2_PracticalPart2.csv")
data1

data1

tibble(
Variable = names(data1),
Description = c("Participant ID number", "Total attendance (in days)", "Conscientiousness (Levels: Low, Moderate, High)", "Time of Class (Levels: 9AM, 10AM, 11AM, 12PM, 1PM, 2PM, 3PM, 4PM)", "Frequency of access to online course materials (Levels: Rarely, Sometimes, Often)", "Year of Study in University (Y1, Y2, Y3, Y4, MSc, PhD)")
) %>% gt::gt()
Variable Description
pid Participant ID number
Attendance Total attendance (in days)
Conscientiousness Conscientiousness (Levels: Low, Moderate, High)
Time Time of Class (Levels: 9AM, 10AM, 11AM, 12PM, 1PM, 2PM, 3PM, 4PM)
OnlineAccess Frequency of access to online course materials (Levels: Rarely, Sometimes, Often)
Year Year of Study in University (Y1, Y2, Y3, Y4, MSc, PhD)

Use this to address RQs 1 and 2.

RQ1 uses the variables Conscientiousness, OnlineAccess, and Year and looks at their associations with Attendance. (… strictly speaking this should be a Poisson model, not a Gaussian)

RQ2 looks at association between Time and Attendance.

data2

tibble(
Variable = names(data2),
Description = c("Final grade (0-100)", "Total attendance (in days)")
) %>% gt::gt()
Variable Description
Marks Final grade (0-100)
Attendance Total attendance (in days)

Use this to address RQ 3.

Set up data

#######
# Coding of Variables
#######

#check coding
str(data1)
spc_tbl_ [397 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ pid              : num [1:397] 1 2 3 4 5 6 7 8 9 10 ...
 $ Attendance       : num [1:397] 9 10 0 8 6 6 9 14 5 10 ...
 $ Conscientiousness: chr [1:397] "High" "High" "Low" "Low" ...
 $ Time             : chr [1:397] "3PM" "2PM" "10AM" "4PM" ...
 $ OnlineAccess     : chr [1:397] "Often" "Often" "Rarely" "Often" ...
 $ Year             : chr [1:397] "Y3" "Y3" "Y2" "Y4" ...
 - attr(*, "spec")=
  .. cols(
  ..   pid = col_double(),
  ..   Attendance = col_double(),
  ..   Conscientiousness = col_character(),
  ..   Time = col_character(),
  ..   OnlineAccess = col_character(),
  ..   Year = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 
str(data2)
spc_tbl_ [200 × 2] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Marks     : num [1:200] 25.2 25.8 25.4 26.4 27.4 ...
 $ Attendance: num [1:200] 10.5 11 11.5 12 12.5 13 13.5 14 14.5 15 ...
 - attr(*, "spec")=
  .. cols(
  ..   Marks = col_double(),
  ..   Attendance = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 
#check for NAs - none in dataset, so no missing values
table(is.na(data1))

FALSE 
 2382 
table(is.na(data2))

FALSE 
  400 

RQ1

set up variables

# make variables factors
data1 <- data1 |>
    mutate(OnlineAccess = as_factor(OnlineAccess),
           Time = as_factor(Time),
           Conscientiousness = as_factor(Conscientiousness),
           Year = as_factor(Year))

#specify reference levels (alternatively use the below tidyverse way like Year - see lecture example code)
data1$OnlineAccess <- relevel(data1$OnlineAccess, "Sometimes")
data1$Conscientiousness <- relevel(data1$Conscientiousness, "Moderate")

#ordering of year variable - make chronological, Y1 as reference group
data1$Year <- data1$Year |>
  factor(levels = c('Y1', 'Y2', 'Y3', 'Y4', 'MSc', 'PhD'))

describe the variables

categ vars

# Look at the marginal distributions of variables - use histograms for continuous outcomes, and barplots for categorical: 

p1 <- ggplot(data1, aes(Attendance)) + 
    geom_histogram() + 
    labs(x = "Attendance", y = "Frequency")

p2 <- ggplot(data1, aes(Conscientiousness)) + 
    geom_bar() + 
    labs(x = "Conscientiousness Level", y = "Frequency")

p3 <- ggplot(data1, aes(Year)) + 
    geom_bar() + 
    labs(x = "Year of Study", y = "Frequency")

p4 <- ggplot(data1, aes(OnlineAccess)) + 
    geom_bar()  + 
    labs(x = "Frequency of Access to Online Materials", y = "Frequency")

p1 / p2 / p3 / p4


# Look at the bivariate associations (note we are also removing the legend - it does not offer the reader any additional information and takes up space):

p5 <- ggplot(data1, aes(x = Conscientiousness, y = Attendance, fill = Conscientiousness)) + 
    geom_boxplot() + 
    labs(x = "Conscientiousness Level", y = "Attendance") + 
    theme(legend.position = "none")

p6 <- ggplot(data1, aes(x = OnlineAccess, y = Attendance, fill = OnlineAccess)) + 
    geom_boxplot() + 
    labs(x = "Frequency of Access to Online Materials", y = "Attendance") + 
    theme(legend.position = "none")

p7 <- ggplot(data1, aes(x = Year, y = Attendance, fill = Year)) + 
    geom_boxplot() + 
    labs(x = "Year of Study", y = "Attendance") + 
    theme(legend.position = "none")

p5 / p6 / p7

num vars

# check how many observations in each category
table(data1$Conscientiousness)

Moderate     High      Low 
     146      124      127 
table(data1$OnlineAccess)

Sometimes     Often    Rarely 
      170       126       101 
table(data1$Year)

 Y1  Y2  Y3  Y4 MSc PhD 
 89 100  66  71  48  23 
# data1 |>
#   group_by(Year, OnlineAccess, Conscientiousness) |>
#   summarise(n = n(), 
#             Mean = mean(Attendance), 
#             SD = sd(Attendance),
#             Minimum = min(Attendance),
#             Maximum = max(Attendance)) |>
#     kable(caption = "Attendance and Academic Year, Frequency of Online Material Access, Conscientiousness Descriptive Statistics", digits = 2) %>%
#     kable_styling()   

^ the kable table is bad.

Visualise Attendance

fit the model

#build model
m1 <- lm(Attendance ~ Conscientiousness + OnlineAccess + Year, data = data1)

#check model summary
summary(m1)

Call:
lm(formula = Attendance ~ Conscientiousness + OnlineAccess + 
    Year, data = data1)

Residuals:
    Min      1Q  Median      3Q     Max 
-37.657  -6.990  -0.279   6.085  31.844 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)             27.874      1.533  18.179  < 2e-16 ***
ConscientiousnessHigh    7.366      1.392   5.292 2.03e-07 ***
ConscientiousnessLow   -10.292      1.399  -7.359 1.12e-12 ***
OnlineAccessOften       -3.533      1.339  -2.639 0.008649 ** 
OnlineAccessRarely      -5.378      1.441  -3.732 0.000218 ***
YearY2                   4.574      1.657   2.760 0.006049 ** 
YearY3                   3.418      1.853   1.844 0.065926 .  
YearY4                   4.266      1.817   2.347 0.019418 *  
YearMSc                  5.649      2.046   2.760 0.006051 ** 
YearPhD                 12.484      2.661   4.692 3.76e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 11.35 on 387 degrees of freedom
Multiple R-squared:  0.3574,    Adjusted R-squared:  0.3424 
F-statistic: 23.91 on 9 and 387 DF,  p-value: < 2.2e-16

check assumptions

# Linearity: Can be assumed as working with categorical predictors

# Independence of Errors: Using a between-subjects design, so can assume this

# Normality (either use plot(model, which = 2) or hist(model$residuals))
plot(m1, which = 2, main = "Normality Assumption Check for m1")


# Equal Variances
residualPlot(m1, main = "Equal Variances Assumption Check for m1")


#### Overall, assumption checks look fine

make nice table for results

tab_model(m1,
          pred.labels = c('Intercept', 'Conscientiousness - High', 'Conscientiousness - Low', 'Online Access - Often', 'Online Access - Rarely', 
                              'UG Y2', 'UG Y3', 'UG Y4', 'MSc', 'PhD'),
          title = "RQ1: Regression Table for Attendance Model")

RQ2

set up the variables

#ordering of time variable - make chronological
data1$Time <- data1$Time |> 
  factor(levels = c('9AM', '10AM', '11AM','12PM', '1PM', '2PM', '3PM', '4PM'))

describe variables

# Numeric
data1 |>
  group_by(Time) |>
  summarise(n = n(), 
            Mean = mean(Attendance), 
            SD = sd(Attendance),
            Minimum = min(Attendance),
            Maximum = max(Attendance)) |>
    kable(caption = "Attendance & Class Time Descriptive Statistics", digits = 2) |>
    kable_styling()    
Attendance & Class Time Descriptive Statistics
Time n Mean SD Minimum Maximum
9AM 56 20.12 10.08 1 49
10AM 48 27.00 14.23 0 60
11AM 46 27.78 14.57 2 52
12PM 47 31.30 14.11 0 55
1PM 45 33.47 14.44 4 60
2PM 46 32.43 12.44 0 60
3PM 52 31.67 13.58 5 54
4PM 57 24.75 13.78 4 54

# check how many observations in each category
table(data1$Time)

 9AM 10AM 11AM 12PM  1PM  2PM  3PM  4PM 
  56   48   46   47   45   46   52   57 
# Visual
p8 <- ggplot(data1, aes(Time)) + 
    geom_bar()

p9 <- ggplot(data1, aes(x = Time, y = Attendance, fill = Time)) + 
    geom_boxplot()

p8 / p9

fit the model

#build model 
m2 <- lm(Attendance ~ Time, data = data1)

#check summary
summary(m2)

Call:
lm(formula = Attendance ~ Time, data = data1)

Residuals:
    Min      1Q  Median      3Q     Max 
-32.435 -10.298   1.327  10.246  33.000 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   20.125      1.793  11.226  < 2e-16 ***
Time10AM       6.875      2.639   2.605  0.00953 ** 
Time11AM       7.658      2.669   2.869  0.00435 ** 
Time12PM      11.173      2.654   4.210 3.17e-05 ***
Time1PM       13.342      2.686   4.968 1.02e-06 ***
Time2PM       12.310      2.669   4.611 5.43e-06 ***
Time3PM       11.548      2.583   4.470 1.03e-05 ***
Time4PM        4.629      2.524   1.834  0.06740 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 13.41 on 389 degrees of freedom
Multiple R-squared:  0.0974,    Adjusted R-squared:  0.08116 
F-statistic: 5.997 on 7 and 389 DF,  p-value: 1.196e-06

check assumptions

# Linearity: Can be assumed as working with categorical predictors

# Independence of Errors: Using a between-subjects design, so can assume this

# Normality (either use plot(model, which = 2) or hist(model$residuals))
plot(m2, which = 2)


# Equal Variances
residualPlot(m2)


#### Overall, assumption checks look fine

test manual contrasts

#Morning/Evening vs Afternoon

#check order
levels(data1$Time)
[1] "9AM"  "10AM" "11AM" "12PM" "1PM"  "2PM"  "3PM"  "4PM" 
#table of weights to present in table 1 analysis strategy
TimePeriod <- c("Early/Late", "Early/Late", "Midday", "Midday", "Midday", "Midday", "Early/Late", "Early/Late")
Time <- c("9AM", "10AM", "11AM", "12PM", "1PM", "2PM", "3PM", "4PM")
Weight <- c(1/4, 1/4, -1/4, -1/4, -1/4, -1/4, 1/4, 1/4)
weights <- tibble(TimePeriod, Time, Weight)


#get means
time_mean <- emmeans(m2, ~Time)

#look at means
time_mean
 Time emmean   SE  df lower.CL upper.CL
 9AM    20.1 1.79 389     16.6     23.6
 10AM   27.0 1.94 389     23.2     30.8
 11AM   27.8 1.98 389     23.9     31.7
 12PM   31.3 1.96 389     27.5     35.1
 1PM    33.5 2.00 389     29.5     37.4
 2PM    32.4 1.98 389     28.5     36.3
 3PM    31.7 1.86 389     28.0     35.3
 4PM    24.8 1.78 389     21.3     28.2

Confidence level used: 0.95 
#plot means
plot(time_mean)


#specify weights for contrast
time_comp <- list('Early or Late vs Middle of the Day' = c(-1/4,-1/4, 1/4, 1/4, 1/4, 1/4, -1/4, -1/4))

#run contrast analysis
time_comp_test <- contrast(time_mean, method = time_comp)

#examine output
time_comp_test
 contrast                           estimate   SE  df t.ratio p.value
 Early or Late vs Middle of the Day     5.36 1.35 389   3.963  0.0001
#obtain confidence intervals
confint(time_comp_test)
 contrast                           estimate   SE  df lower.CL upper.CL
 Early or Late vs Middle of the Day     5.36 1.35 389      2.7     8.01

Confidence level used: 0.95 

RQ3

set up data

I guess it’s fine

describe data

data2 |>
    describe() |>
    select(2:4, 8:9) |>
    rename("N" = n, "Mean" = mean, "SD" = sd, "Minimum" = min, "Maximum" = max) |>    
        kable(caption = "Final Grades & Attendance Descriptive Statistics", digits = 2) |>
        kable_styling()  
Final Grades & Attendance Descriptive Statistics
N Mean SD Minimum Maximum
Marks 200 49.79 15.84 25.01 98.2
Attendance 200 35.25 14.47 10.50 60.0

data2 |>
    select(Attendance, Marks) |>
    cor() |>
    round(digits = 2)
           Attendance Marks
Attendance       1.00  0.91
Marks            0.91  1.00
ggplot(data = data2, aes(x = Attendance, y = Marks)) + 
    geom_point() + 
    geom_smooth(method = "lm", se = FALSE) + 
    labs(x = "Attendance (in days)", y = "Final Grade")

fit the model

#specify model
m3 <- lm(Marks ~ Attendance, data = data2)

#check summary
summary(m3)

Call:
lm(formula = Marks ~ Attendance, data = data2)

Residuals:
     Min       1Q   Median       3Q      Max 
-17.1477  -4.5210  -0.1861   4.2501  26.8415 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 14.83270    1.25534   11.82   <2e-16 ***
Attendance   0.99163    0.03296   30.09   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.727 on 198 degrees of freedom
Multiple R-squared:  0.8205,    Adjusted R-squared:  0.8196 
F-statistic: 905.3 on 1 and 198 DF,  p-value: < 2.2e-16

check assumptions

# Linearity (can also use plot(model, which = 1) in place of below)
ggplot(data2, aes(x = Attendance, y = Marks)) + 
    geom_point() + 
    geom_smooth(method = 'lm', se = F) + 
    geom_smooth(method = 'loess', se = F, colour = 'red') + 
    labs(x = "Attendance", y = "Final Grade", title = "Scatterplot with linear (blue) and loess (red) lines")


# Independence of Errors: Using a between-subjects design, so can assume this

# Normality (either use plot(model, which = 2) or hist(model$residuals))
plot(m3, which = 2)


# Equal Variances
residualPlot(m3)

bootstrap model

# use 1000 resamples
boot_m3 <- Boot(m3, R = 1000)
G3;Loading required namespace: boot
g
#check summary
summary(boot_m3)

Number of bootstrap replications R = 1000 
            original   bootBias   bootSE  bootMed
(Intercept) 14.83270 -0.0462723 1.042487 14.78770
Attendance   0.99163  0.0019082 0.036449  0.99322
#confidence intervals
confint(boot_m3)
Bootstrap bca confidence intervals
LS0tCnRpdGxlOiAiTGVjdHVyZSAxMCBwbGF5Z3JvdW5kIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKLS0tCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KHBlcmZvcm1hbmNlKQpsaWJyYXJ5KHBhdGNod29yaykKYGBgCgojIyBSZXNlYXJjaCBxdWVzdGlvbnMKCi0gUlExOiBEb2VzIGNvbnNjaWVudGlvdXNuZXNzLCBmcmVxdWVuY3kgb2YgYWNjZXNzIHRvIG9ubGluZSBtYXRlcmlhbHMsIGFuZCB5ZWFyIG9mIHN0dWR5IGluIFVuaXZlcnNpdHkgcHJlZGljdCBjb3Vyc2UgYXR0ZW5kYW5jZT8KLSBSUTI6IElzIHRoZXJlIGEgZGlmZmVyZW5jZSBpbiBhdHRlbmRhbmNlIGJldHdlZW4gdGhvc2Ugd2l0aCBlYXJseS9sYXRlIGNsYXNzZXMgaW4gY29tcGFyaXNvbiB0byB0aG9zZSB3aXRoIG1pZGRheSBjbGFzc2VzPwotIFJRMzogSXMgY2xhc3MgYXR0ZW5kYW5jZSBhc3NvY2lhdGVkIHdpdGggZmluYWwgZ3JhZGVzPwoKCiMjIERhdGEgTWFuYWdlbWVudAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBsb2FkIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBmb3IgYWxsIHRoaW5ncyEKbGlicmFyeShwc3ljaCkgIyBnb29kIGZvciBkZXNjcmlwdGl2ZSBzdGF0cwpsaWJyYXJ5KHBhdGNod29yaykgIyBncm91cGluZyBwbG90cyB0b2dldGhlcgpsaWJyYXJ5KGthYmxlRXh0cmEpICMgdXNlZnVsIGZvciBjcmVhdGluZyBuaWNlIHRhYmxlcwpsaWJyYXJ5KHNqUGxvdCkgI3JlZ3Jlc3Npb24gdGFibGVzICYgcGxvdHMKbGlicmFyeShlbW1lYW5zKSAjZm9yIGNvbnRyYXN0cwpsaWJyYXJ5KGNhcikgI2ZvciBhc3N1bXB0aW9ucyAoY3JQbG90cywgcmVzaWR1YWxQbG90cywgVklGKSBhbmQgYm9vdHN0cmFwcGluZwoKIyByZWFkIGluIGRhdGFzZXRzCmRhdGExIDwtIHJlYWRfY3N2KCJodHRwczovL3VvZXBzeS5naXRodWIuaW8vZGF0YS9EYXBSMl9TMUIyX1ByYWN0aWNhbFBhcnQxLmNzdiIpCmRhdGEyIDwtIHJlYWRfY3N2KCJodHRwczovL3VvZXBzeS5naXRodWIuaW8vZGF0YS9EYXBSMl9TMUIyX1ByYWN0aWNhbFBhcnQyLmNzdiIpCgpgYGAKCgpgYGB7cn0KZGF0YTEKYGBgCgojIyMgZGF0YTEKCmBgYHtyfQp0aWJibGUoClZhcmlhYmxlID0gbmFtZXMoZGF0YTEpLApEZXNjcmlwdGlvbiA9IGMoIlBhcnRpY2lwYW50IElEIG51bWJlciIsICJUb3RhbCBhdHRlbmRhbmNlIChpbiBkYXlzKSIsICJDb25zY2llbnRpb3VzbmVzcyAoTGV2ZWxzOiBMb3csIE1vZGVyYXRlLCBIaWdoKSIsICJUaW1lIG9mIENsYXNzIChMZXZlbHM6IDlBTSwgMTBBTSwgMTFBTSwgMTJQTSwgMVBNLCAyUE0sIDNQTSwgNFBNKSIsICJGcmVxdWVuY3kgb2YgYWNjZXNzIHRvIG9ubGluZSBjb3Vyc2UgbWF0ZXJpYWxzIChMZXZlbHM6IFJhcmVseSwgU29tZXRpbWVzLCBPZnRlbikiLCAiWWVhciBvZiBTdHVkeSBpbiBVbml2ZXJzaXR5IChZMSwgWTIsIFkzLCBZNCwgTVNjLCBQaEQpIikKKSAlPiUgZ3Q6Omd0KCkKYGBgCgpVc2UgdGhpcyB0byBhZGRyZXNzIFJRcyAxIGFuZCAyLgoKUlExIHVzZXMgdGhlIHZhcmlhYmxlcyBgQ29uc2NpZW50aW91c25lc3NgLCBgT25saW5lQWNjZXNzYCwgYW5kIGBZZWFyYCBhbmQgbG9va3MgYXQgdGhlaXIgYXNzb2NpYXRpb25zIHdpdGggYEF0dGVuZGFuY2VgLgooLi4uIHN0cmljdGx5IHNwZWFraW5nIHRoaXMgc2hvdWxkIGJlIGEgUG9pc3NvbiBtb2RlbCwgbm90IGEgR2F1c3NpYW4pCgpSUTIgbG9va3MgYXQgYXNzb2NpYXRpb24gYmV0d2VlbiBgVGltZWAgYW5kIGBBdHRlbmRhbmNlYC4KCgoKIyMjIGRhdGEyCgpgYGB7cn0KdGliYmxlKApWYXJpYWJsZSA9IG5hbWVzKGRhdGEyKSwKRGVzY3JpcHRpb24gPSBjKCJGaW5hbCBncmFkZSAoMC0xMDApIiwgIlRvdGFsIGF0dGVuZGFuY2UgKGluIGRheXMpIikKKSAlPiUgZ3Q6Omd0KCkKYGBgCgpVc2UgdGhpcyB0byBhZGRyZXNzIFJRIDMuCgoKIyMgU2V0IHVwIGRhdGEKCmBgYHtyfQojIyMjIyMjCiMgQ29kaW5nIG9mIFZhcmlhYmxlcwojIyMjIyMjCgojY2hlY2sgY29kaW5nCnN0cihkYXRhMSkKc3RyKGRhdGEyKQoKI2NoZWNrIGZvciBOQXMgLSBub25lIGluIGRhdGFzZXQsIHNvIG5vIG1pc3NpbmcgdmFsdWVzCnRhYmxlKGlzLm5hKGRhdGExKSkKdGFibGUoaXMubmEoZGF0YTIpKQpgYGAKCiMjIFJRMQoKIyMjIHNldCB1cCB2YXJpYWJsZXMKCmBgYHtyfQojIG1ha2UgdmFyaWFibGVzIGZhY3RvcnMKZGF0YTEgPC0gZGF0YTEgfD4KICAgIG11dGF0ZShPbmxpbmVBY2Nlc3MgPSBhc19mYWN0b3IoT25saW5lQWNjZXNzKSwKICAgICAgICAgICBUaW1lID0gYXNfZmFjdG9yKFRpbWUpLAogICAgICAgICAgIENvbnNjaWVudGlvdXNuZXNzID0gYXNfZmFjdG9yKENvbnNjaWVudGlvdXNuZXNzKSwKICAgICAgICAgICBZZWFyID0gYXNfZmFjdG9yKFllYXIpKQoKI3NwZWNpZnkgcmVmZXJlbmNlIGxldmVscyAoYWx0ZXJuYXRpdmVseSB1c2UgdGhlIGJlbG93IHRpZHl2ZXJzZSB3YXkgbGlrZSBZZWFyIC0gc2VlIGxlY3R1cmUgZXhhbXBsZSBjb2RlKQpkYXRhMSRPbmxpbmVBY2Nlc3MgPC0gcmVsZXZlbChkYXRhMSRPbmxpbmVBY2Nlc3MsICJTb21ldGltZXMiKQpkYXRhMSRDb25zY2llbnRpb3VzbmVzcyA8LSByZWxldmVsKGRhdGExJENvbnNjaWVudGlvdXNuZXNzLCAiTW9kZXJhdGUiKQoKI29yZGVyaW5nIG9mIHllYXIgdmFyaWFibGUgLSBtYWtlIGNocm9ub2xvZ2ljYWwsIFkxIGFzIHJlZmVyZW5jZSBncm91cApkYXRhMSRZZWFyIDwtIGRhdGExJFllYXIgfD4KICBmYWN0b3IobGV2ZWxzID0gYygnWTEnLCAnWTInLCAnWTMnLCAnWTQnLCAnTVNjJywgJ1BoRCcpKQpgYGAKCiMjIyBkZXNjcmliZSB0aGUgdmFyaWFibGVzCgojIyMjIGNhdGVnIHZhcnMKCmBgYHtyfQojIExvb2sgYXQgdGhlIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbnMgb2YgdmFyaWFibGVzIC0gdXNlIGhpc3RvZ3JhbXMgZm9yIGNvbnRpbnVvdXMgb3V0Y29tZXMsIGFuZCBiYXJwbG90cyBmb3IgY2F0ZWdvcmljYWw6IAoKcDEgPC0gZ2dwbG90KGRhdGExLCBhZXMoQXR0ZW5kYW5jZSkpICsgCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsgCiAgICBsYWJzKHggPSAiQXR0ZW5kYW5jZSIsIHkgPSAiRnJlcXVlbmN5IikKCnAyIDwtIGdncGxvdChkYXRhMSwgYWVzKENvbnNjaWVudGlvdXNuZXNzKSkgKyAKICAgIGdlb21fYmFyKCkgKyAKICAgIGxhYnMoeCA9ICJDb25zY2llbnRpb3VzbmVzcyBMZXZlbCIsIHkgPSAiRnJlcXVlbmN5IikKCnAzIDwtIGdncGxvdChkYXRhMSwgYWVzKFllYXIpKSArIAogICAgZ2VvbV9iYXIoKSArIAogICAgbGFicyh4ID0gIlllYXIgb2YgU3R1ZHkiLCB5ID0gIkZyZXF1ZW5jeSIpCgpwNCA8LSBnZ3Bsb3QoZGF0YTEsIGFlcyhPbmxpbmVBY2Nlc3MpKSArIAogICAgZ2VvbV9iYXIoKSAgKyAKICAgIGxhYnMoeCA9ICJGcmVxdWVuY3kgb2YgQWNjZXNzIHRvIE9ubGluZSBNYXRlcmlhbHMiLCB5ID0gIkZyZXF1ZW5jeSIpCgpwMSAvIHAyIC8gcDMgLyBwNAoKIyBMb29rIGF0IHRoZSBiaXZhcmlhdGUgYXNzb2NpYXRpb25zIChub3RlIHdlIGFyZSBhbHNvIHJlbW92aW5nIHRoZSBsZWdlbmQgLSBpdCBkb2VzIG5vdCBvZmZlciB0aGUgcmVhZGVyIGFueSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFuZCB0YWtlcyB1cCBzcGFjZSk6CgpwNSA8LSBnZ3Bsb3QoZGF0YTEsIGFlcyh4ID0gQ29uc2NpZW50aW91c25lc3MsIHkgPSBBdHRlbmRhbmNlLCBmaWxsID0gQ29uc2NpZW50aW91c25lc3MpKSArIAogICAgZ2VvbV9ib3hwbG90KCkgKyAKICAgIGxhYnMoeCA9ICJDb25zY2llbnRpb3VzbmVzcyBMZXZlbCIsIHkgPSAiQXR0ZW5kYW5jZSIpICsgCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwNiA8LSBnZ3Bsb3QoZGF0YTEsIGFlcyh4ID0gT25saW5lQWNjZXNzLCB5ID0gQXR0ZW5kYW5jZSwgZmlsbCA9IE9ubGluZUFjY2VzcykpICsgCiAgICBnZW9tX2JveHBsb3QoKSArIAogICAgbGFicyh4ID0gIkZyZXF1ZW5jeSBvZiBBY2Nlc3MgdG8gT25saW5lIE1hdGVyaWFscyIsIHkgPSAiQXR0ZW5kYW5jZSIpICsgCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwNyA8LSBnZ3Bsb3QoZGF0YTEsIGFlcyh4ID0gWWVhciwgeSA9IEF0dGVuZGFuY2UsIGZpbGwgPSBZZWFyKSkgKyAKICAgIGdlb21fYm94cGxvdCgpICsgCiAgICBsYWJzKHggPSAiWWVhciBvZiBTdHVkeSIsIHkgPSAiQXR0ZW5kYW5jZSIpICsgCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwNSAvIHA2IC8gcDcKYGBgCgojIyMjIG51bSB2YXJzCgpgYGB7cn0KIyBjaGVjayBob3cgbWFueSBvYnNlcnZhdGlvbnMgaW4gZWFjaCBjYXRlZ29yeQp0YWJsZShkYXRhMSRDb25zY2llbnRpb3VzbmVzcykKdGFibGUoZGF0YTEkT25saW5lQWNjZXNzKQp0YWJsZShkYXRhMSRZZWFyKQoKIyBkYXRhMSB8PgojICAgZ3JvdXBfYnkoWWVhciwgT25saW5lQWNjZXNzLCBDb25zY2llbnRpb3VzbmVzcykgfD4KIyAgIHN1bW1hcmlzZShuID0gbigpLCAKIyAgICAgICAgICAgICBNZWFuID0gbWVhbihBdHRlbmRhbmNlKSwgCiMgICAgICAgICAgICAgU0QgPSBzZChBdHRlbmRhbmNlKSwKIyAgICAgICAgICAgICBNaW5pbXVtID0gbWluKEF0dGVuZGFuY2UpLAojICAgICAgICAgICAgIE1heGltdW0gPSBtYXgoQXR0ZW5kYW5jZSkpIHw+CiMgICAgIGthYmxlKGNhcHRpb24gPSAiQXR0ZW5kYW5jZSBhbmQgQWNhZGVtaWMgWWVhciwgRnJlcXVlbmN5IG9mIE9ubGluZSBNYXRlcmlhbCBBY2Nlc3MsIENvbnNjaWVudGlvdXNuZXNzIERlc2NyaXB0aXZlIFN0YXRpc3RpY3MiLCBkaWdpdHMgPSAyKSAlPiUKIyAgICAga2FibGVfc3R5bGluZygpICAgCgpgYGAKCl4gdGhlIGthYmxlIHRhYmxlIGlzIGJhZC4KClZpc3VhbGlzZSBgQXR0ZW5kYW5jZWAKCmBgYHtyfQpkYXRhMSB8PgogIGdncGxvdChhZXMoeCA9IEF0dGVuZGFuY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKCgoKIyMjIGZpdCB0aGUgbW9kZWwKCmBgYHtyfQojYnVpbGQgbW9kZWwKbTEgPC0gbG0oQXR0ZW5kYW5jZSB+IENvbnNjaWVudGlvdXNuZXNzICsgT25saW5lQWNjZXNzICsgWWVhciwgZGF0YSA9IGRhdGExKQoKI2NoZWNrIG1vZGVsIHN1bW1hcnkKc3VtbWFyeShtMSkKYGBgCgoKIyMjIGNoZWNrIGFzc3VtcHRpb25zCgpgYGB7cn0KIyBMaW5lYXJpdHk6IENhbiBiZSBhc3N1bWVkIGFzIHdvcmtpbmcgd2l0aCBjYXRlZ29yaWNhbCBwcmVkaWN0b3JzCgojIEluZGVwZW5kZW5jZSBvZiBFcnJvcnM6IFVzaW5nIGEgYmV0d2Vlbi1zdWJqZWN0cyBkZXNpZ24sIHNvIGNhbiBhc3N1bWUgdGhpcwoKIyBOb3JtYWxpdHkgKGVpdGhlciB1c2UgcGxvdChtb2RlbCwgd2hpY2ggPSAyKSBvciBoaXN0KG1vZGVsJHJlc2lkdWFscykpCnBsb3QobTEsIHdoaWNoID0gMiwgbWFpbiA9ICJOb3JtYWxpdHkgQXNzdW1wdGlvbiBDaGVjayBmb3IgbTEiKQoKIyBFcXVhbCBWYXJpYW5jZXMKcmVzaWR1YWxQbG90KG0xLCBtYWluID0gIkVxdWFsIFZhcmlhbmNlcyBBc3N1bXB0aW9uIENoZWNrIGZvciBtMSIpCgojIyMjIE92ZXJhbGwsIGFzc3VtcHRpb24gY2hlY2tzIGxvb2sgZmluZQoKYGBgCgojIyMgbWFrZSBuaWNlIHRhYmxlIGZvciByZXN1bHRzCgpgYGB7cn0KdGFiX21vZGVsKG0xLAogICAgICAgICAgcHJlZC5sYWJlbHMgPSBjKCdJbnRlcmNlcHQnLCAnQ29uc2NpZW50aW91c25lc3MgLSBIaWdoJywgJ0NvbnNjaWVudGlvdXNuZXNzIC0gTG93JywgJ09ubGluZSBBY2Nlc3MgLSBPZnRlbicsICdPbmxpbmUgQWNjZXNzIC0gUmFyZWx5JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdVRyBZMicsICdVRyBZMycsICdVRyBZNCcsICdNU2MnLCAnUGhEJyksCiAgICAgICAgICB0aXRsZSA9ICJSUTE6IFJlZ3Jlc3Npb24gVGFibGUgZm9yIEF0dGVuZGFuY2UgTW9kZWwiKQpgYGAKCgojIyBSUTIKCiMjIyBzZXQgdXAgdGhlIHZhcmlhYmxlcwoKYGBge3J9CiNvcmRlcmluZyBvZiB0aW1lIHZhcmlhYmxlIC0gbWFrZSBjaHJvbm9sb2dpY2FsCmRhdGExJFRpbWUgPC0gZGF0YTEkVGltZSB8PiAKICBmYWN0b3IobGV2ZWxzID0gYygnOUFNJywgJzEwQU0nLCAnMTFBTScsJzEyUE0nLCAnMVBNJywgJzJQTScsICczUE0nLCAnNFBNJykpCmBgYAoKIyMjIGRlc2NyaWJlIHZhcmlhYmxlcwoKYGBge3J9CiMgTnVtZXJpYwpkYXRhMSB8PgogIGdyb3VwX2J5KFRpbWUpIHw+CiAgc3VtbWFyaXNlKG4gPSBuKCksIAogICAgICAgICAgICBNZWFuID0gbWVhbihBdHRlbmRhbmNlKSwgCiAgICAgICAgICAgIFNEID0gc2QoQXR0ZW5kYW5jZSksCiAgICAgICAgICAgIE1pbmltdW0gPSBtaW4oQXR0ZW5kYW5jZSksCiAgICAgICAgICAgIE1heGltdW0gPSBtYXgoQXR0ZW5kYW5jZSkpIHw+CiAgICBrYWJsZShjYXB0aW9uID0gIkF0dGVuZGFuY2UgJiBDbGFzcyBUaW1lIERlc2NyaXB0aXZlIFN0YXRpc3RpY3MiLCBkaWdpdHMgPSAyKSB8PgogICAga2FibGVfc3R5bGluZygpICAgIAoKIyBjaGVjayBob3cgbWFueSBvYnNlcnZhdGlvbnMgaW4gZWFjaCBjYXRlZ29yeQp0YWJsZShkYXRhMSRUaW1lKQoKIyBWaXN1YWwKcDggPC0gZ2dwbG90KGRhdGExLCBhZXMoVGltZSkpICsgCiAgICBnZW9tX2JhcigpCgpwOSA8LSBnZ3Bsb3QoZGF0YTEsIGFlcyh4ID0gVGltZSwgeSA9IEF0dGVuZGFuY2UsIGZpbGwgPSBUaW1lKSkgKyAKICAgIGdlb21fYm94cGxvdCgpCgpwOCAvIHA5CmBgYAoKCiMjIyBmaXQgdGhlIG1vZGVsCgpgYGB7cn0KI2J1aWxkIG1vZGVsIAptMiA8LSBsbShBdHRlbmRhbmNlIH4gVGltZSwgZGF0YSA9IGRhdGExKQoKI2NoZWNrIHN1bW1hcnkKc3VtbWFyeShtMikKYGBgCgojIyMgY2hlY2sgYXNzdW1wdGlvbnMKCmBgYHtyfQojIExpbmVhcml0eTogQ2FuIGJlIGFzc3VtZWQgYXMgd29ya2luZyB3aXRoIGNhdGVnb3JpY2FsIHByZWRpY3RvcnMKCiMgSW5kZXBlbmRlbmNlIG9mIEVycm9yczogVXNpbmcgYSBiZXR3ZWVuLXN1YmplY3RzIGRlc2lnbiwgc28gY2FuIGFzc3VtZSB0aGlzCgojIE5vcm1hbGl0eSAoZWl0aGVyIHVzZSBwbG90KG1vZGVsLCB3aGljaCA9IDIpIG9yIGhpc3QobW9kZWwkcmVzaWR1YWxzKSkKcGxvdChtMiwgd2hpY2ggPSAyKQoKIyBFcXVhbCBWYXJpYW5jZXMKcmVzaWR1YWxQbG90KG0yKQoKIyMjIyBPdmVyYWxsLCBhc3N1bXB0aW9uIGNoZWNrcyBsb29rIGZpbmUKCmBgYAoKIyMjIHRlc3QgbWFudWFsIGNvbnRyYXN0cwoKYGBge3J9CiNNb3JuaW5nL0V2ZW5pbmcgdnMgQWZ0ZXJub29uCgojY2hlY2sgb3JkZXIKbGV2ZWxzKGRhdGExJFRpbWUpCgojdGFibGUgb2Ygd2VpZ2h0cyB0byBwcmVzZW50IGluIHRhYmxlIDEgYW5hbHlzaXMgc3RyYXRlZ3kKVGltZVBlcmlvZCA8LSBjKCJFYXJseS9MYXRlIiwgIkVhcmx5L0xhdGUiLCAiTWlkZGF5IiwgIk1pZGRheSIsICJNaWRkYXkiLCAiTWlkZGF5IiwgIkVhcmx5L0xhdGUiLCAiRWFybHkvTGF0ZSIpClRpbWUgPC0gYygiOUFNIiwgIjEwQU0iLCAiMTFBTSIsICIxMlBNIiwgIjFQTSIsICIyUE0iLCAiM1BNIiwgIjRQTSIpCldlaWdodCA8LSBjKDEvNCwgMS80LCAtMS80LCAtMS80LCAtMS80LCAtMS80LCAxLzQsIDEvNCkKd2VpZ2h0cyA8LSB0aWJibGUoVGltZVBlcmlvZCwgVGltZSwgV2VpZ2h0KQoKCiNnZXQgbWVhbnMKdGltZV9tZWFuIDwtIGVtbWVhbnMobTIsIH5UaW1lKQoKI2xvb2sgYXQgbWVhbnMKdGltZV9tZWFuCgojcGxvdCBtZWFucwpwbG90KHRpbWVfbWVhbikKCiNzcGVjaWZ5IHdlaWdodHMgZm9yIGNvbnRyYXN0CnRpbWVfY29tcCA8LSBsaXN0KCdFYXJseSBvciBMYXRlIHZzIE1pZGRsZSBvZiB0aGUgRGF5JyA9IGMoLTEvNCwtMS80LCAxLzQsIDEvNCwgMS80LCAxLzQsIC0xLzQsIC0xLzQpKQoKI3J1biBjb250cmFzdCBhbmFseXNpcwp0aW1lX2NvbXBfdGVzdCA8LSBjb250cmFzdCh0aW1lX21lYW4sIG1ldGhvZCA9IHRpbWVfY29tcCkKCiNleGFtaW5lIG91dHB1dAp0aW1lX2NvbXBfdGVzdAoKI29idGFpbiBjb25maWRlbmNlIGludGVydmFscwpjb25maW50KHRpbWVfY29tcF90ZXN0KQpgYGAKCiMjIFJRMyAKCiMjIyBzZXQgdXAgZGF0YQoKSSBndWVzcyBpdCdzIGZpbmUKCiMjIyBkZXNjcmliZSBkYXRhCgpgYGB7cn0KZGF0YTIgfD4KICAgIGRlc2NyaWJlKCkgfD4KICAgIHNlbGVjdCgyOjQsIDg6OSkgfD4KICAgIHJlbmFtZSgiTiIgPSBuLCAiTWVhbiIgPSBtZWFuLCAiU0QiID0gc2QsICJNaW5pbXVtIiA9IG1pbiwgIk1heGltdW0iID0gbWF4KSB8PiAgICAKICAgICAgICBrYWJsZShjYXB0aW9uID0gIkZpbmFsIEdyYWRlcyAmIEF0dGVuZGFuY2UgRGVzY3JpcHRpdmUgU3RhdGlzdGljcyIsIGRpZ2l0cyA9IDIpIHw+CiAgICAgICAga2FibGVfc3R5bGluZygpICAKCmRhdGEyIHw+CiAgICBzZWxlY3QoQXR0ZW5kYW5jZSwgTWFya3MpIHw+CiAgICBjb3IoKSB8PgogICAgcm91bmQoZGlnaXRzID0gMikKCmdncGxvdChkYXRhID0gZGF0YTIsIGFlcyh4ID0gQXR0ZW5kYW5jZSwgeSA9IE1hcmtzKSkgKyAKICAgIGdlb21fcG9pbnQoKSArIAogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKyAKICAgIGxhYnMoeCA9ICJBdHRlbmRhbmNlIChpbiBkYXlzKSIsIHkgPSAiRmluYWwgR3JhZGUiKQpgYGAKCiMjIyBmaXQgdGhlIG1vZGVsCgpgYGB7cn0KI3NwZWNpZnkgbW9kZWwKbTMgPC0gbG0oTWFya3MgfiBBdHRlbmRhbmNlLCBkYXRhID0gZGF0YTIpCgojY2hlY2sgc3VtbWFyeQpzdW1tYXJ5KG0zKQpgYGAKCiMjIyBjaGVjayBhc3N1bXB0aW9ucwoKYGBge3J9CiMgTGluZWFyaXR5IChjYW4gYWxzbyB1c2UgcGxvdChtb2RlbCwgd2hpY2ggPSAxKSBpbiBwbGFjZSBvZiBiZWxvdykKZ2dwbG90KGRhdGEyLCBhZXMoeCA9IEF0dGVuZGFuY2UsIHkgPSBNYXJrcykpICsgCiAgICBnZW9tX3BvaW50KCkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScsIHNlID0gRikgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsb2VzcycsIHNlID0gRiwgY29sb3VyID0gJ3JlZCcpICsgCiAgICBsYWJzKHggPSAiQXR0ZW5kYW5jZSIsIHkgPSAiRmluYWwgR3JhZGUiLCB0aXRsZSA9ICJTY2F0dGVycGxvdCB3aXRoIGxpbmVhciAoYmx1ZSkgYW5kIGxvZXNzIChyZWQpIGxpbmVzIikKCiMgSW5kZXBlbmRlbmNlIG9mIEVycm9yczogVXNpbmcgYSBiZXR3ZWVuLXN1YmplY3RzIGRlc2lnbiwgc28gY2FuIGFzc3VtZSB0aGlzCgojIE5vcm1hbGl0eSAoZWl0aGVyIHVzZSBwbG90KG1vZGVsLCB3aGljaCA9IDIpIG9yIGhpc3QobW9kZWwkcmVzaWR1YWxzKSkKcGxvdChtMywgd2hpY2ggPSAyKQoKIyBFcXVhbCBWYXJpYW5jZXMKcmVzaWR1YWxQbG90KG0zKQoKYGBgCgojIyMgYm9vdHN0cmFwIG1vZGVsCgpgYGB7cn0KIyB1c2UgMTAwMCByZXNhbXBsZXMKYm9vdF9tMyA8LSBCb290KG0zLCBSID0gMTAwMCkKCiNjaGVjayBzdW1tYXJ5CnN1bW1hcnkoYm9vdF9tMykKCiNjb25maWRlbmNlIGludGVydmFscwpjb25maW50KGJvb3RfbTMpCmBgYAoKCgoKCgo=